﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Apworks;
using TinyForum.Infrastructure;
using TinyForum.Domain.Events.Account;

namespace TinyForum.Domain.Model
{
    /// <summary>
    /// Represents the account of the forum.
    /// </summary>
    public class Account : EventSourceAggregateRoot
    {
        #region Private Fields
        private string userName;
        private string password;
        private string displayName;
        private string avatar;
        private string email;
        private DateTime dateJoined;
        private DateTime? dateLastLogon;
        private bool isAdmin;

        private bool isOnline;
        private bool isLocked;
        private bool isApproved;
        private DateTime? dateLastLocked;
        private DateTime? dateLastPasswordChanged;
        private string passwordQuestion;
        private string passwordAnswer;
        #endregion

        #region Ctor
        /// <summary>
        /// Initializes a new instance of <c>User</c> class.
        /// </summary>
        protected Account()
        {

        }
        /// <summary>
        /// Initializes a new instance of <c>User</c> class.
        /// </summary>
        /// <param name="userName">The user name which identifies the user.</param>
        /// <param name="password">The password for the user to be authenticated.</param>
        /// <param name="displayName">The display name of the user.</param>
        /// <param name="avatar">The URI address of the picture which represents the avatar of the user.</param>
        /// <param name="email">The email of the user.</param>
        /// <param name="isAdmin">A <see cref="System.Boolean"/> value which indicates whether the user is an administrator.</param>
        public Account(string userName, string password, string displayName, string avatar, string email, bool isAdmin, bool isOnline,
            bool isLocked, bool isApproved, string passwordQuestion, string passwordAnswer)
        {
            this.userName = userName;
            this.password = password;
            this.displayName = displayName;
            this.avatar = avatar;
            this.email = email;
            this.isAdmin = isAdmin;
            this.dateJoined = DateTime.Now;
            this.dateLastLogon = null;
            this.isOnline = isOnline;
            this.isLocked = isLocked;
            this.isApproved = isApproved;
            this.passwordQuestion = passwordQuestion;
            this.passwordAnswer = passwordAnswer;
            this.dateLastLocked = null;
            this.dateLastPasswordChanged = null;
        }
        /// <summary>
        /// Initializes a new instance of <c>User</c> class.
        /// </summary>
        /// <param name="userName">The user name which identifies the user.</param>
        /// <param name="password">The password for the user to be authenticated.</param>
        /// <param name="displayName">The display name of the user.</param>
        /// <param name="email">The email of the user.</param>
        public Account(string userName, string password, string displayName, string email, string passwordQuestion, string passwordAnswer)
            : this(userName, password, displayName, null, email, false, false, false, true, passwordQuestion, passwordAnswer)
        {

        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets or sets the user name of the user used for authentication.
        /// </summary>
        public virtual string UserName
        {
            get { return this.userName; }
            set { this.userName = value; }
        }
        /// <summary>
        /// Gets the password of the user used for authentication.
        /// </summary>
        public virtual string Password
        {
            get { return this.password; }
        }
        /// <summary>
        /// Gets or sets the display name of the user.
        /// </summary>
        public virtual string DisplayName
        {
            get { return this.displayName; }
            set
            {
                this.displayName = value;
                Publish(new DisplayNameChangedEvent(this.ID, this.userName, value));
            }
        }
        /// <summary>
        /// Gets or sets the URI of the picture which represents the avatar 
        /// for the user.
        /// </summary>
        public virtual string Avatar
        {
            get { return this.avatar; }
            set
            {
                this.avatar = value;
                Publish(new AvatarChangedEvent(this.ID, this.userName, value));
            }
        }
        /// <summary>
        /// Gets or sets the email address of the user.
        /// </summary>
        public virtual string Email
        {
            get { return this.email; }
            set
            {
                this.email = value;
                Publish(new EmailChangedEvent(this.ID, this.userName, value));
            }

        }
        /// <summary>
        /// Gets the <see cref="System.DateTime"/> value which indicates
        /// when the user was joined the forum.
        /// </summary>
        public virtual DateTime DateJoined
        {
            get { return this.dateJoined; }
        }
        /// <summary>
        /// Gets the <see cref="System.DateTime"/> value which indicates
        /// the date and time that the user logged on for the last time.
        /// </summary>
        public virtual DateTime? DateLastLogon
        {
            get { return this.dateLastLogon; }
        }
        /// <summary>
        /// Gets or sets a <see cref="System.Boolean"/> value which indicates whether
        /// the current user is a system administrator.
        /// </summary>
        public virtual bool IsAdmin
        {
            get { return this.isAdmin; }
            set { this.isAdmin = value; }
        }

        public virtual bool IsOnline
        {
            get { return this.isOnline; }
            set { this.isOnline = value; }
        }

        public virtual bool IsLocked
        {
            get { return this.isLocked; }
            set { this.isLocked = value; }
        }

        public virtual bool IsApproved
        {
            get { return this.isApproved; }
            set { this.isApproved = value; }
        }

        public virtual DateTime? DateLastLocked
        {
            get { return this.dateLastLocked; }
            set { this.dateLastLocked = value; }
        }

        public virtual DateTime? DateLastPasswordChanged
        {
            get { return this.dateLastPasswordChanged; }
            set { this.dateLastPasswordChanged = value; }
        }

        public virtual string PasswordQuestion
        {
            get { return this.passwordQuestion; }
        }

        public virtual string PasswordAnswer
        {
            get { return this.passwordAnswer; }
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Changes the password.
        /// </summary>
        /// <param name="oldPassword">The old password for verification.</param>
        /// <param name="newPassword">The new password that is going to be used by the account.</param>
        /// <returns>True if the password was changed successfully, otherwise false.</returns>
        public virtual bool ChangePassword(string oldPassword, string newPassword)
        {
            if (this.password != oldPassword)
                return false;
            this.password = newPassword;
            this.dateLastPasswordChanged = DateTime.Now;
            Publish(new PasswordChangedEvent(this.ID, this.userName, newPassword));
            return true;
        }
        /// <summary>
        /// Changes the question and answer for password recovery, for example, reset password.
        /// </summary>
        /// <param name="newQuestion">The new question to be used.</param>
        /// <param name="newAnswer">The new answer to be used.</param>
        public virtual void ChangePasswordQuestionAndAnswer(string newQuestion, string newAnswer)
        {
            this.passwordQuestion = newQuestion;
            this.passwordAnswer = newAnswer;
            Publish(new PasswordQAChangedEvent(this.ID, this.userName, newQuestion, newAnswer));
        }
        /// <summary>
        /// Resets the password.
        /// </summary>
        /// <param name="passwordAnswer">The answer to the password question.</param>
        /// <returns>The new password, or null, if the reset was unsuccessful.</returns>
        public virtual string ResetPassword(string passwordAnswer)
        {
            if (this.passwordAnswer == passwordAnswer)
            {
                string newPassword = Apworks.Utils.GetUniqueIdentifier(8);
                this.password = newPassword;
                this.dateLastPasswordChanged = DateTime.Now;
                Publish(new ResetPasswordEvent(this.ID, this.userName, newPassword));
                return newPassword;
            }
            return null;
        }
        /// <summary>
        /// Locks the current account.
        /// </summary>
        public virtual void Lock()
        {
            this.isLocked = true;
            this.dateLastLocked = DateTime.Now;
        }
        /// <summary>
        /// Unlock the current account.
        /// </summary>
        public virtual void Unlock()
        {
            this.isLocked = false;
        }

        public override string ToString()
        {
            return this.userName;
        }
        #endregion
    }
}
